/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author           Notes
 * 2019-07-06     heyuanjie87      the first version
 */

#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>
#include <rthw.h>

/* STM32 uart driver */
struct stm32_uart
{
    UART_HandleTypeDef UartHandle;
    IRQn_Type irq;

    char * uart_name;
    struct rt_serial_device serial;
    int32_t rde_pin;
    short re_level;
    short halfduplex;
};

RT_WEAK void stm32_uart_rde(void *ins, int pin, int val)
{
    //rt_pin_write(pin, val);
    (void)ins;
}

static void stm32_intrx(struct stm32_uart *uart, int en)
{
    if (en)
    {
        stm32_uart_rde(&uart->UartHandle, uart->rde_pin, uart->re_level);
        __HAL_UART_ENABLE_IT(&uart->UartHandle, UART_IT_RXNE);
    }
    else
    {
        __HAL_UART_DISABLE_IT(&uart->UartHandle, UART_IT_RXNE);
    }
}

static void stm32_inttx(struct stm32_uart *uart, int en)
{
    if (en)
    {
        stm32_uart_rde(&uart->UartHandle, uart->rde_pin, !uart->re_level);
        __HAL_UART_ENABLE_IT(&uart->UartHandle, UART_IT_TXE);
    } 
    else
    {
        __HAL_UART_DISABLE_IT(&uart->UartHandle, UART_IT_TXE);
        __HAL_UART_ENABLE_IT(&uart->UartHandle, UART_IT_TC);
    }
}

static int stm32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct stm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    uart->UartHandle.Init.BaudRate     = cfg->baud_rate;
    uart->UartHandle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
    uart->UartHandle.Init.Mode         = UART_MODE_TX_RX;
    uart->UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;

    switch (cfg->data_bits)
    {
    case DATA_BITS_8:
        uart->UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
        break;
    case DATA_BITS_9:
        uart->UartHandle.Init.WordLength = UART_WORDLENGTH_9B;
        break;
    default:
        uart->UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
        break;
    }

    switch (cfg->stop_bits)
    {
    case STOP_BITS_1:
        uart->UartHandle.Init.StopBits   = UART_STOPBITS_1;
        break;
    case STOP_BITS_2:
        uart->UartHandle.Init.StopBits   = UART_STOPBITS_2;
        break;
    default:
        uart->UartHandle.Init.StopBits   = UART_STOPBITS_1;
        break;
    }

    switch (cfg->parity)
    {
    case PARITY_NONE:
        uart->UartHandle.Init.Parity     = UART_PARITY_NONE;
        break;
    case PARITY_ODD:
        uart->UartHandle.Init.Parity     = UART_PARITY_ODD;
        break;
    case PARITY_EVEN:
        uart->UartHandle.Init.Parity     = UART_PARITY_EVEN;
        break;
    default:
        uart->UartHandle.Init.Parity     = UART_PARITY_NONE;
        break;
    }

    if (cfg->parity != PARITY_NONE)
        uart->UartHandle.Init.WordLength = UART_WORDLENGTH_9B;

    if (HAL_UART_Init(&uart->UartHandle) != HAL_OK)
    {
        return -1;
    }

    return 0;
}

static int stm32_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    int ret = 0;
    struct stm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);

    uart = (struct stm32_uart *)serial->parent.user_data;

    switch (cmd)
    {
    default:
    {
        ret = -1;
    }break;
    }

    return ret;
}

static int stm32_putc(struct rt_serial_device *serial, char c)
{
    struct stm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    uart->UartHandle.Instance->TDR = c;
    //while ((__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_TC) == RESET));

    return 1;
}

static int stm32_getc(struct rt_serial_device *serial)
{
    int ch;
    struct stm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    ch = -1;
    if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_RXNE) != RESET)
        ch = uart->UartHandle.Instance->RDR & 0xff;

    return ch;
}

static int stm32_init(struct rt_serial_device *serial)
{
    struct stm32_uart *uart;

    uart = (struct stm32_uart *)serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);
    if (uart->halfduplex)
    {
        rt_pin_mode(uart->rde_pin, PIN_MODE_OUTPUT);
    }

    HAL_NVIC_SetPriority(uart->irq, 0, 1);
    HAL_NVIC_EnableIRQ(uart->irq);

    return 0;
}

static void stm32_deinit(struct rt_serial_device *serial)
{
    struct stm32_uart *uart;

    uart = (struct stm32_uart *)serial->parent.user_data;

    HAL_UART_DeInit(&uart->UartHandle);
}

static int stm32_xmitctl(struct rt_serial_device *serial, int cmd, int arg)
{
    int ret = 0;
    struct stm32_uart *uart;

    uart = (struct stm32_uart *)serial->parent.user_data;

    switch (cmd)
    {
    case UART_XMIT_RX:
    {
        stm32_intrx(uart, arg);
    }break;
    case UART_XMIT_TX:
    {
        stm32_inttx(uart, arg);
    }break;
    default:
    {
        ret = -1;
    }break;
    }

    return ret;
}

/**
 * Uart common interrupt process. This need add to uart ISR.
 *
 * @param serial serial device
 */
static void uart_isr(struct rt_serial_device *serial)
{
    struct stm32_uart *uart;

    /* enter interrupt */
    rt_interrupt_enter();

    uart = (struct stm32_uart *) serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);

    /* UART in mode Receiver -------------------------------------------------*/
    if ((__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_RXNE) != RESET) &&
        (__HAL_UART_GET_IT_SOURCE(&uart->UartHandle, UART_IT_RXNE) != RESET))
    {
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
        /* Clear RXNE interrupt flag */
        __HAL_UART_CLEAR_FLAG(&uart->UartHandle, UART_FLAG_RXNE);
    }

    if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_ORE) != RESET)
    {
        __HAL_UART_CLEAR_OREFLAG(&uart->UartHandle);
    }
    if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_NE) != RESET)
    {
         __HAL_UART_CLEAR_NEFLAG(&uart->UartHandle);
    }
    if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_FE) != RESET)
    {
        __HAL_UART_CLEAR_FEFLAG(&uart->UartHandle);
    }
    if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_PE) != RESET)
    {
        __HAL_UART_CLEAR_PEFLAG(&uart->UartHandle);
    }
    if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_TXE) && 
        __HAL_UART_GET_IT_SOURCE(&uart->UartHandle, UART_IT_TXE))
    {
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE);
    }

    if (__HAL_UART_GET_FLAG(&uart->UartHandle, UART_FLAG_TC) && 
        __HAL_UART_GET_IT_SOURCE(&uart->UartHandle, UART_IT_TC))
    {
        __HAL_UART_DISABLE_IT(&uart->UartHandle, UART_IT_TC);
        stm32_uart_rde(uart->UartHandle.Instance, uart->rde_pin, uart->re_level);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

static const struct rt_uart_ops _uart_ops =
{
    stm32_configure,
    stm32_control,
    stm32_putc,
    stm32_getc,
    stm32_init,
    stm32_deinit,
    stm32_xmitctl
};

#if defined(BSP_USING_UART1)
/* UART1 device driver structure */
static struct stm32_uart uart1 =
{
    {USART1},               // UART_HandleTypeDef UartHandle;
    USART1_IRQn,            // IRQn_Type irq;
    "uart1",                // char * uart_name;
};

void USART1_IRQHandler(void)
{
    uart_isr(&uart1.serial);
}
#endif /* BSP_USING_UART1 */

#if defined(BSP_USING_UART2)
/* UART2 device driver structure */
static struct stm32_uart uart2 =
{
    {USART2},               // UART_HandleTypeDef UartHandle;
    USART2_IRQn,            // IRQn_Type irq;
    "uart2",                // char * uart_name;
};

void USART2_IRQHandler(void)
{
    uart_isr(&uart2.serial);
}
#endif /* BSP_USING_UART2 */

#if defined(BSP_USING_UART3)
/* UART3 device driver structure */
static struct stm32_uart uart3 =
{
    {USART3},               // UART_HandleTypeDef UartHandle;
    USART3_IRQn,            // IRQn_Type irq;
    "uart3",                // char * uart_name;
};

void USART3_IRQHandler(void)
{
    uart_isr(&uart3.serial);
}
#endif /* BSP_USING_UART3 */

#if defined(BSP_USING_UART5)
/* UART3 device driver structure */
static struct stm32_uart uart5 =
{
    {UART5},               // UART_HandleTypeDef UartHandle;
    UART5_IRQn,            // IRQn_Type irq;
    "uart5",                // char * uart_name;
};

void UART5_IRQHandler(void)
{
    uart_isr(&uart5.serial);
}
#endif /* BSP_USING_UART5 */

#ifdef RT_USING_CONSOLE
static int uart_rs485_init(struct stm32_uart *u, const char *rdepin, int relv);

static struct stm32_uart* stm32_console_get(void)
{
    struct stm32_uart *uh = 0;

#if (BSP_CONSOLE_UART_N == 1)
    uh = &uart1;
#elif (BSP_CONSOLE_UART_N == 2)
    uh = &uart2;
#elif (BSP_CONSOLE_UART_N == 3)
    uh = &uart3;
#elif (BSP_CONSOLE_UART_N == 5)
    uh = &uart5;
#endif

    return uh;
}

void rt_hw_console_output(char c)
{
    struct stm32_uart *uh;

    uh = stm32_console_get();
    if (!uh)
        return;

    stm32_uart_rde(0, uh->rde_pin, !uh->re_level);

    uh->UartHandle.Instance->TDR = c;
    while ((__HAL_UART_GET_FLAG(&uh->UartHandle, UART_FLAG_TC) == RESET));

    stm32_uart_rde(0, uh->rde_pin, uh->re_level);
}

int rt_hw_console_init(void)
{
    struct stm32_uart *stu = stm32_console_get();
    UART_HandleTypeDef *uh;

    if (!stu)
        return -1;

    uh = &stu->UartHandle;

#if ((BSP_CONSOLE_UART_N == 2) && defined(BSP_UART2_HALFDUPLEX))
    uart_rs485_init(&uart2, BSP_UART2_RDE_PIN, BSP_UART2_RE_LEVEL);
#endif

    uh->Init.BaudRate     = 115200;
    uh->Init.HwFlowCtl    = UART_HWCONTROL_NONE;
    uh->Init.Mode         = UART_MODE_TX_RX;
    uh->Init.OverSampling = UART_OVERSAMPLING_16;
    uh->Init.WordLength = UART_WORDLENGTH_8B;
    uh->Init.StopBits   = UART_STOPBITS_1;
    uh->Init.Parity     = UART_PARITY_NONE;

    return HAL_UART_Init(uh);
}
INIT_BOARD_EXPORT(rt_hw_console_init);
#endif

static int uart_rs485_init(struct stm32_uart *u, const char *rdepin, int relv)
{
    u->halfduplex = 1;
    u->rde_pin = rt_pin_get(rdepin);
    if (u->rde_pin < 0)
        return -1;

    u->re_level = (short)relv;

    return 0;
}

int stm32_hw_usart_init(void)
{
    int i;
    struct stm32_uart* uarts[] =
    {
#ifdef BSP_USING_UART1
        &uart1,
#endif
#ifdef BSP_USING_UART2
        &uart2,
#endif
#ifdef BSP_USING_UART3
        &uart3,
#endif
#ifdef BSP_USING_UART5
        &uart5,
#endif
    };

#if defined(BSP_USING_UART1)
#if defined(BSP_UART1_HALFDUPLEX)
//    if (uart_rs485_init(&uart1, BSP_UART1_RDE_PIN, BSP_UART1_RE_LEVEL) != 0)
    {
      //  rt_kprintf("uart1 rs485 init fail\r\n");
        //return -1;
    }
#endif
#endif

#if defined(BSP_USING_UART2)
#if defined(BSP_UART2_HALFDUPLEX)
    if (uart_rs485_init(&uart2, BSP_UART2_RDE_PIN, BSP_UART2_RE_LEVEL) != 0)
    {
        rt_kprintf("uart2 rs485 init fail\r\n");
        return -1;
    }
#endif
#endif

    for (i = 0; i < sizeof(uarts) / sizeof(uarts[0]); i++)
    {
        struct stm32_uart *uart = uarts[i];
        int result;

        uart->serial.ops = &_uart_ops;

        /* register UART device */
        result = rt_hw_serial_register(&uart->serial,
                              uart->uart_name,
                              0,
                              uart);

        RT_ASSERT(result == 0);
        (void)result;
    }

    return 0;
}
INIT_DEVICE_EXPORT(stm32_hw_usart_init);
